<?php
class Student extends AppModel {

    public $recursive = -1;
    public $name = 'Student';
    public $actsAs = array(
        'Archivable',
        'Searchable' => array(
            'fields' => array(
                'name', 'number', 'gender', 'class',
                'degree', 'political', 'academic',
                'student_minority_id' => 'StudentMinority.name',
                'student_province_id' => 'StudentProvince.name'
            ),
            'name' => 'name'
        )
    );
    public $validate = array(
        'number' => array('notempty'),
        'name' => array('notempty')
    );

    // the Associations below have been created with all possible keys,
    // those that are not needed can be removed
    public $belongsTo = array(
        'StudentMinority' => array(
            'className' => 'StudentMinority',
            'foreignKey' => 'student_minority_id',
            'counterCache' => true,
            'conditions' => '',
            'fields' => '',
            'order' => ''
        ),
        'StudentProvince' => array(
            'className' => 'StudentProvince',
            'foreignKey' => 'student_province_id',
            'counterCache' => true,
            'conditions' => '',
            'fields' => '',
            'order' => ''
        )
    );

    public $hasOne = array(
        'StudentResume' => array(
            'className' => 'StudentResume',
            'foreignKey' => 'student_id',
            'dependent' => true,
            'conditions' => '',
            'fields' => '',
            'order' => ''
        )
    );

    public $hasAndBelongsToMany = array(
        'Job' => array(
            'className' => 'Job',
            'joinTable' => 'students_jobs',
            'foreignKey' => 'student_id',
            'associationForeignKey' => 'job_id',
            'counterCache' => true,
            'dependent' => true,
            'unique' => true,
            'conditions' => '',
            'fields' => '',
            'order' => '',
            'limit' => '',
            'offset' => '',
            'finderQuery' => '',
            'deleteQuery' => '',
            'insertQuery' => ''
        )
    );

    /* Read data for job application management */
    public function getJobData($id){
        if(!$id) return false;
        $student = $this->find('first', array(
            'conditions'=>array('Student.id'=>$id),
            'contain' =>array(
                'Job' =>array('Company'=>array(
                    'fields'=>array('id', 'name', 'website', 'email', 'job_count'))
                )
            )
        ));
        return $student;
    }

    /**
     * Perform CSV file parsing and student data import, user add, etc :
     *
     * Import student data to the database, here we have some tricks,
     * As the uploaded file does not belong to any Model, we assign it to the temp user,
     * that is to say, each data file to import is treated and transfered as an User Attachment,
     * after importing of data, we just keep every imported file and that makes up an histroy,
     * model.User: in charge of file upload and retrieve;
     * model.Student: incharge of file parsing and data importing
     */
    public function import($options = array()){
        //$this->Behaviors->detach('Searchable');
        extract($options);
        $logs =& $logs;
        $this->Group =& $group;
        $this->User =& $user;
        unset($options['group'], $options['user'], $options['data']);

        $logs[] = 'Start transfer for uploaded data file...';
        if($this->User->saveAll($data, array('validate'=>'first'))){
            $logs[] = 'Success: File transfer success';
            $logs[] = 'Now retrieve ImportFile from temp user ...';
            $attachment = $this->User->getImportFile();
            if($attachment){
                $attachment = $errors['attachment'] = $attachment['Attachment'];
                $logs[] = 'Success: ImportFile retrieve success';
                $logs[] = 'Now generating ImportFile name ...';
                $file = MEDIA . $attachment['dirname'] . DS . $attachment['basename'];
                $logs[] = 'ImportFile is ' . $attachment['basename'];
                $options['file'] = $file;
                extract($this->__import($options));
            }else{
                $logs[] = 'Error: ImportFile retrieve failure, be sure the temp user exists';
                $logs[] = 'Now abort student data import transaction';
            }
        }else{
            $logs[] = 'Error: File transfer failure';
            $logs[] = 'Now abort student data import transaction';
        }
        return compact('logs');
    }
    private function __import($options = array()){
        extract($options); $logs =& $logs;

        /* Retrieve GroupID for student */
        $logs[] = 'Retrieve Group.student.id  ...';
        $group_id = $this->Group->field('id', 'Group.name="student"');
        $logs[] = 'Group.student.id is: ' . $group_id;

        $logs[] = 'Now start reading data file ...';
        $handle = fopen($file, "r");

        $logs[] = 'Reading ImportFile header ...';
        $header = fgetcsv($handle);

        $i = 0;
        /* read each data row in the file */
        $logs[] = 'Loop ImportFile content ...';
        while(($row = fgetcsv($handle)) !== false){
                $i++; $data = array();
                $logs[] = 'Start Reading data row: ' . $i;
                /* for each header field */
                foreach($header as $k=>$head){
                    /* get the data field from Model.field */
                    if(strpos($head, '.') !== false){
                        $h = explode('.', $head);
                        $data[$h[0]][$h[1]] = (isset($row[$k])) ? $row[$k] : '';
                    /* get the data field from field */
                    }else{
                        $data['Student'][$head]=(isset($row[$k])) ? $row[$k] : '';
                    }
                }
                /* keep an reference of current student number, save bytes */
                $sn = $data['Student']['number'];
                /* adjust student birthday to avoid illegal inputs */
                if(isset($data['Student']['birthday'])){
                    $birthday = date('Y-m-d', strtotime($data['Student']['birthday']));
                    $logs[] = sprintf(__('Adjusting birthday from %s=>%s', true), $data['Student']['birthday'], $birthday);
                    $data['Student']['birthday'] = $birthday;
                }
                /* Check for unique student number */
                $logs[] = sprintf(__('Now checking for Student %s existence...', true), $sn);
                $student = $this->findByNumber($sn);
                /* If the specified student number does not exist, we create a new one */
                if(!$student){
                    $errors[] = sprintf(__('Success: Student with ID %s does not exists', true), $sn);
                    /* prepare special fields, such as province, minority */
                    $data = $this->__justifySpecialImportFields($data, &$logs);
                    /* validate the student */
                    $this->set($data);
                    $logs[] = sprintf(__('Now validate data for Student %s...', true), $sn);
                    if(!$this->validates()){
                        $errors[] = sprintf(__('Error: Student with ID %s failed to validate', true), $sn);
                    }else{
                        $logs[] = sprintf(__('Success: Student with ID %s passed validate',true), $sn);
                        /* save the student */
                        $this->create();
                        $logs[] = sprintf(__('Now create Student %s...', true), $sn);
                        if(!$this->save($data)){
                            $errors[] = sprintf(__('Error: Student with ID %s failed to save', true), $sn);
                            continue;
                        }else{
                            $logs[] = sprintf(__('Success: Student with ID %s was saved',true), $sn);
                            $this->log(sprintf(__('New student: %s', true), $sn), $log);
                            /* check for user existence */
                            $logs[] = sprintf(__('Now checking for User %s existence...', true), $sn);
                            if(!$this->User->findByUsername($sn)){
                                $logs[] = sprintf(__('Now start creating new User %s...', true), $sn);
                                $user = array('User'=>array(
                                    'group_id' => $group_id,
                                    'username' => $sn,
                                    'password' => $password
                                ));
                                $email = $data['Student']['email'];
                                if(!empty($email)){
                                    $logs[] = sprintf(__('New user has email: %s', true), $email);
                                    $user['User']['email'] = $email;
                                    unset($email);
                                }
                                /* validate user */
                                $logs[] = sprintf(__('Now validate data for User %s...', true), $sn);
                                $this->User->set($user);
                                if(!$this->User->validates()){
                                    $logs[] = sprintf(__('Error: User %s failed to validate', true), $sn);
                                    continue;
                                }else{
                                    $logs[] = sprintf(__('Success: User %s passed validate', true), $sn);
                                    $logs[] = sprintf(__('Now saving User %s...', true), $sn);
                                    /* save user */
                                    $this->User->create();
                                    if(!$this->User->save($user)){
                                        $logs[] = sprintf(__('Error: User %s failed to save', true), $sn);
                                        continue;
                                    }else{
                                        $logs[] = sprintf(__('Success: User %s was saved', true), $sn);
                                        $this->log(sprintf(__('New User: %s', true),  $sn), $log);
                                    }
                                }
                            }else{
                                $logs[] = sprintf(__('Omit: StudentUser %s already exists', true), $sn);
                                continue;
                            }
                        }
                    }
                }elseif($merge && !empty($sn)){
                    /* prepare special fields, such as province, minority */
                    $data = $this->__justifySpecialImportFields($data, &$logs);
                    $logs[] = sprintf(__('Merge: trying to update student with ID %s...',true), $sn);
                    $this->id = $student['Student']['id'];
                    if($this->save($data)){
                        $logs[] = sprintf(__('Success: update student with ID %s',true), $sn);
                        $this->log(sprintf(__('Update student information with ID %s',true), $sn), $log);
                        $logs[] = $this->__updateUserEmail($data['Student']);
                    }else{
                        $logs[] = sprintf(__('Error: failed to update student with ID %s',true), $sn);
                    }
                    continue;
                }else{
                    $logs[] = sprintf(__('Omit: Student with ID %s already exists',true), $sn);
                    continue;
                }
        }

        /* close the file */
        fclose($handle);

        /* return log messages */
        return compact('logs');
    }
    /* Update user email that corresponds to the current student */
    private function __updateUserEmail($data, $import=true){
        if(empty($data['email']) || empty($data['number'])){
            return __('Error: student.number or user.email not specified', true);
        }
        $this->User->id = $this->User->field('id', 'User.username="'.$data['number'].'"');
        $this->User->set(array('email'=>$data['email']));
        if($this->User->save()){
            return sprintf(__('Success: user email updated: %s:%s', true), $data['number'], $data['email']);
        }else{
            return __('Error: user email update failed', true);
        }
    }
    /* Justify special import fields, such as province, minority, political, gender */
    private function __justifySpecialImportFields($data, &$logs){
        $sn = $data['Student']['number'];
        $logs[] = sprintf(__('Start processing special fields for Student %s ...', true), $sn);
        if(!empty($data['Student']['student_minority_id'])){
            $logs[] = sprintf(__('Processing student_minority_id for Student %s ...', true), $sn);
            App::import('StudentMinority');
            $minority = new StudentMinority();
            $minorities = $minority->find('list');
            $id = $this->__getSpecialFieldId($minorities, $data['Student']['student_minority_id']);
            $logs[] = sprintf(__('Get student_minority_id for Student %s as %s=>%s', true), $sn, $id, $data['Student']['student_minority_id']);
            $data['Student']['student_minority_id'] = $id;
        }
        if(!empty($data['Student']['student_province_id'])){
            $logs[] = sprintf(__('Processing student_province_id for Student %s ...', true), $sn);
            App::import('StudentProvince');
            $province = new StudentProvince();
            $provinces = $province->find('list');
            $id = $this->__getSpecialFieldId($provinces, $data['Student']['student_province_id']);
            $logs[] = sprintf(__('Get student_province_id for Student %s as %s=>%s', true), $sn, $id, $data['Student']['student_province_id']);
            $data['Student']['student_province_id'] = $id;
        }
        $logs[] = sprintf(__('Finished processing special fields for Student %s ...', true), $sn);
        return $data;
    }
    private function __getSpecialFieldId($list, $value){
        foreach($list as $id=>$compare){
            if($compare == $value){
                return $id;
            }
        }
    }

    /*
     * Retrieve table fields or just query for requested data;
     *
     * Param: @state: before for fields retrieve, after for data query
     * it may perform further queires when generate export conditions,
     * such as export by Student.enrollYear, classNumber, etc;
     */
    public function export($state='before'){
        $blacklist = array('created', 'modified', 'active', 'resume', 'students_job_count');
        $orders = array('id'=>__('Id', true), 'number'=>__('Number', true));
        if($state == 'before'){
            $schema = array(
                'basic' => array('name', 'gender', 'birthday', 'student_minority_id', 'student_province_id'),
                'profile' => array('number', 'class', 'academic', 'degree'),
                'contact'=> array('email', 'phone'),
                'extra'=> array('id')
            );
            return compact('schema', 'blacklist', 'orders');
        }elseif(is_array($state)){
            $this->Behaviors->attach('Containable');
            $data = $this->find('all', array_merge(array(
                'contain' => array(
                    'StudentMinority.name',
                    'StudentProvince.name')
            ), $state));
            $this->Behaviors->detach('Containable');
            return $this->__export($data, $state['fields']);
        }else{
            return false;
        }
    }
    /* Generate csv content for export */
    private function __export($students, $fields){
        $content = '';
        foreach($students as $student){
            $student = $this->__justifySpecialExportFields($student, $fields);
            $content .= implode(",", $student['Student']);
            $content .= ",\n";
        }
        $header = implode(",", $fields);
        $header .= ",\n";
        return $header . $content;
    }
    /* Justify special export fields, such as Provinces and Minorities */
    private function __justifySpecialExportFields($data, $fields){
        $relations = array(
            'student_minority_id' => 'StudentMinority',
            'student_province_id' => 'StudentProvince');
        foreach($relations as $fk=>$model){
            if(array_search($fk, $fields) !== false){
                $data['Student'][$fk] = $data[$model]['name'];
                unset($data[$model]);
            }
        }
        return $data;
    }
}
?>